/************************************************************************
Copyright 2003, 2004, 2005, 2006, 2007, 2008 Grid Technology Research Center,
National Institute of Advanced Industrial Science and Technology.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
************************************************************************/

package jp.aist.gtrc.plus.scheduler.specific.sge.sched;

import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;

import jp.aist.gtrc.plus.scheduler.specific.sge.jaxb.common.JGElement;
import jp.aist.gtrc.plus.scheduler.specific.sge.status.SGEHostGroup;
import jp.aist.gtrc.plus.scheduler.specific.sge.status.SGEJobID;
import jp.aist.gtrc.plus.scheduler.specific.sge.status.SGENodeStatus;
import jp.aist.gtrc.plus.scheduler.specific.sge.status.SGEParallelEnv;
import jp.aist.gtrc.plus.scheduler.specific.sge.status.SGEProject;
import jp.aist.gtrc.plus.scheduler.specific.sge.status.SGEQInstance;
import jp.aist.gtrc.plus.scheduler.specific.sge.status.SGEQueueStatus;
import jp.aist.gtrc.plus.scheduler.specific.sge.status.SGEStatusManager;
import jp.aist.gtrc.plus.scheduler.specific.sge.status.SGETaskStatus;
import jp.aist.gtrc.plus.scheduler.specific.sge.status.SGEUserset;
import jp.aist.gtrc.plus.scheduler.status.JobStatus;
import jp.aist.gtrc.plus.scheduler.status.MainServer;
import jp.aist.gtrc.plus.scheduler.status.NodeStatus;
import jp.aist.gtrc.plus.scheduler.status.QInstanceStatus;
import jp.aist.gtrc.plus.scheduler.status.QueueStatus;
import jp.aist.gtrc.plus.scheduler.sys.SchedulerException;
import jp.aist.gtrc.plus.scheduler.util.LastTimeChecker;

public final class SGESelfStatusManager extends SGEStatusManager {

	private LinkedHashMap<String, SGEUserset> usersets = null;
	private LinkedHashMap<String, SGEProject> projs = null;
	private LinkedHashMap<String, SGEParallelEnv> pes = null;
	private LinkedHashMap<String, SGEHostGroup> hgroups = null;
	private final LinkedHashMap<String, NodeStatus> qInstances;
	private final HashSet<SGEJobID> dirtyTasks;
	private final LastTimeChecker lastChecker;
	private static final  int SYSINFO_CHECK_PERIOD = 300; // [sec]

	private SGESelfStatusManager() {

		super();

		qInstances = new LinkedHashMap<String, NodeStatus>();
		dirtyTasks = new HashSet<SGEJobID>();
		lastChecker = new LastTimeChecker(SYSINFO_CHECK_PERIOD);

	}

	public static synchronized SGEStatusManager getInstance() {

		SGEStatusManager mgr = getInstanceForOverride();
		if (mgr == null){
			mgr = new SGESelfStatusManager();
			setInstance(mgr);
		}
		return mgr;

	}

	protected void getAllJobs(MainServer srv) throws SchedulerException {

		jobsOnQins.clear();
		jobsOnNode.clear();

		for (JobStatus j : srv.getJobStatus()) {
			assert(j instanceof SGETaskStatus);
			SGETaskStatus task = (SGETaskStatus)j;
			Collection<JGElement> grantQs = task.getGrantedQueues();
			if (grantQs.isEmpty()) {
				addJob(task);
			} else {
				for (JGElement jg : task.getGrantedQueues()) {
					addJob(task, jg.getJGQname(), jg.getJGQhostname());
				}
			}
		}

	}

	private void updateSysInfo(MainServer srv) throws SchedulerException{

		if (lastChecker.mustUpdate()) {
			SGESelfMainServer server = (SGESelfMainServer)srv;
			usersets = server.getUsersets();
			projs = server.getProjects();
			pes = server.getParallelEnvs();
			hgroups = server.getHostGroups();
		}

	}

	public synchronized void updateStatus(MainServer srv) throws SchedulerException{

		if (srv instanceof SGESelfMainServer) {
			updateSysInfo(srv);
			super.updateStatus(srv);
			updateQInstances();
			updateDirtyTasks();
		} else {
			super.updateStatus(srv);
		}

	}

	private void updateQInstances() throws SchedulerException {

		qInstances.clear();

		for (QueueStatus q : getQueues()) {
			SGEQueueStatus queue = (SGEQueueStatus)q;
			for (QInstanceStatus qi : queue.getAllQInstances()) {
				SGEQInstance qins = (SGEQInstance)qi;
				qInstances.put(qins.getFullName(), qins);

				// node has qIns on it, qIns has node which is on.
				String nodeName = qins.getHostname();
				SGENodeStatus node = (SGENodeStatus)super.getNode(nodeName);
				if (node != null) {
					node.addQInstance(qins);
					qins.setNodeStatus(node);
				} else {
					throw new SchedulerException(
							"cannot find execnode for " + qins.getFullName()
							+ ", check domain names of 'qconf -sel' and 'qstat -f'.");
				}
			}
		}

	}

	private void updateDirtyTasks() {

		dirtyTasks.clear();

		/*
		 * NOTE: user super.getNodes() to get SGENodeStatus,
		 * not use this.getNodes() which returns SGEQinstance.
		 */
		for (NodeStatus n : super.getNodes()) {
			SGENodeStatus node = (SGENodeStatus)n;
			Collection<SGEJobID> dirties = node.getDirtyTasks();
			if (dirties == null)
				continue;
			for (SGEJobID jobID : dirties) {
				dirtyTasks.add(jobID);
			}
		}

	}

	public synchronized boolean isDirtyTask(SGEJobID jobID) {

		return dirtyTasks.contains(jobID);

	}

	public synchronized Collection<NodeStatus> getNodes(){

		/*
		 * We use SGEQInstance as NodeStatus, not SGENodeStatus.
		 * 'rsvNodeMgr' has Collection<SGENodeStatus>
		 */
		return qInstances.values();

	}

	public synchronized HashMap<String, NodeStatus> getNodeMap(){

		//return nodes;
		return qInstances;

	}

	public synchronized NodeStatus getNode(String nodeName) {

		//return nodes.get(nodeName);
		/*
		 * key of 'qInstances' map is SGEQInstance.getFullName()
		 * which maybe "all.q@host00.example.com" etc.
		 * 'nodeName' will be these, not "host00.example.com"
		 * because getNodes() returns 'qInstances' map.
		 * 'nodeName' must have '@', check it by indexOf().
		 */
		assert(isQFullname(nodeName) == true);
		return qInstances.get(nodeName);

	}

	public synchronized HashMap<String, SGEUserset> getUsersetMap() {

		return usersets;

	}

	public synchronized SGEUserset getUserset(String usersetName) {

		return (usersets != null) ? usersets.get(usersetName) : null;

	}

	public synchronized HashMap<String, SGEProject> getProjectMap() {

		return projs;

	}

	public synchronized SGEProject getProject(String projName) {

		return (projs != null) ? projs.get(projName) : null;

	}

	public synchronized HashMap<String, SGEParallelEnv> getParallelEnvMap() {

		return pes;

	}

	public synchronized SGEParallelEnv getParallelEnv(String peName) {

		return (pes != null) ? pes.get(peName) : null;

	}

	public synchronized HashMap<String, SGEHostGroup> getHostGroupMap() {

		return hgroups;

	}

	public synchronized SGEHostGroup getHostGroup(String hgroupName) {

		return (hgroups != null) ? hgroups.get(hgroupName) : null;

	}

}
